#ifndef YIGSYNTHGRAPHIC_H
#define YIGSYNTHGRAPHIC_H

// System includes
#include <QObject>
#include <QPoint>
#include <QImage>
#include <QGraphicsItem>
#include <QPen>
#include <QPainterPath>
#include <QBrush>
#include <string>
#include <QGraphicsEllipseItem>
#include <QGraphicsSimpleTextItem>
#include <QPixmap>

// Local includes
#include "graphics/yigcablegraphic.h"
#include "graphics/yigattractor.h"

/**
*   YigSynthGraphic is the graphics representation of YigSynth in the YigGraphicsView
*/
class YigSynthGraphic : public YigBaseGraphic, public QGraphicsEllipseItem
{
    Q_OBJECT

public:

    /**
    *   YigSynthGraphic constructor
    *   @param QString mapKey for the YigNetworkChannel map
    *   @param const QPointF& point Starting position of the EllipseItem
    *   @param float the randomized first parameter
    *   @param float the randomized second parameter
    *   @param int The output bus for the synth, used for oscilliscope animation
    *   @param QGraphicsScene the scene in which this synth resides
    */
    YigSynthGraphic(QString synthType, QString mapKey, const QPointF& point, float param1, float param2,
                    int outBus, QGraphicsScene* scene);

    /**
    *   YigSynthGraphic deconstructor
    */
    ~YigSynthGraphic();

    /**
    *   get the brush of this instance
    *   @return QBrush The brush that defines the color of this YigSynthGraphics
    */
    QBrush getBrush();


    /**
    *   Get the key for this item to the YigNetworkChannel map
    *   @return QString map key
    */
    QString getMapKey();

    /**
    *   setSynthType Set the type of synth (sin, filter, sample, etc... that this graphic represents
    *   @param QString synthType The name of the type of synth
    */
    void setSynthType(QString synthType);

    /**
    *   getSynthType Get the type of synth (sin, filter, sample, etc... that this graphic represents
    *   @return QString synthType The name of the type of synth
    */
    QString getSynthType();

    /**
    *   getAudioBus returns the output bus for this Synth
    *   @return int The number of the bus
    */
    int getAudioOutBus();

    /**
    *   setAudioBus sets the output bus for this Synth
    *   @param int The number of the bus
    */
    void setAudioOutBus(int bus);

    /**
    *   update the position of this graphic, used to emit posChanged for networking
    *   @param QPointF& point the new position
    */
    void updatePos( const QPointF& point );

    /**
    *    causes this synth to update its current position to the network
    */
    void triggerUpdatePos();

    /**
    *   keyboardDelete(), used when a child YigEllipseItem is keyboard cut/deleted
    */
    void keyboardDelete();

    /**
    *   itemClick When the user clicks on one of the small ellipses (audioInput,etc..) check the type and emit ellipseClicked
    *   @param QGraphicsItem* clicked item pointer to the ellipse that was clicked, checked against internal pointers to
    *   find the type of ellipse that was clicked, AUDIO_INPUT, AUDIO_OUTPUT, or MOD_INPUT
    */
    void itemClick(QGraphicsItem* item);

    /**
    *   addAudioInputCable Add the pointer to the cable in this synthGraphic, which will facilitate movement updates
    *   @param YigCableGraphic* cable the YigNetworkChannel map key for the cable
    */
    void addAudioInputCable(YigCableGraphic* cable);

    /**
    *   addAudioOutputCable Add the pointer to the cable in this synthGraphic, which will facilitate movement updates
    *   @param YigCableGraphic* cable the YigNetworkChannel map key for the cable
    */
    void addAudioOutputCable(YigCableGraphic* cable);

    /**
    *   addModInputCable Add the pointer to the cable in this synthGraphic, which will facilitate movement updates
    *   @param YigCableGraphic* cable the YigNetworkChannel map key for the cable
    */
    void addModInputCable(YigCableGraphic* cable);

    /**
    *   removeAudioInputCable Remove the pointer to the cable in this synthGraphic
    *   @param QString cableMapKey the YigNetworkChannel map key for the cable
    *   @return bool Test for whether the cable was found, and subsequently deleted
    */
    bool removeAudioInputCable(QString cableMapKey);

    /**
    *   removeAudioOutputCable Remove the pointer to the cable in this synthGraphic
    *   @param QString cableMapKey the YigNetworkChannel map key for the cable
    *   @return bool Test for whether the cable was found, and subsequently deleted
    */
    bool removeAudioOutputCable(QString cableMapKey);

    /**
    *   removeModInputCable Remove the pointer to the cable in this synthGraphic
    *   @param QString cableMapKey the YigNetworkChannel map key for the cable
    *   @return bool Test for whether the cable was found, and subsequently deleted
    */
    bool removeModInputCable(QString cableMapKey);

    /**
    *   removeCable Remove the pointer to the cable in this synthGraphic, use this only if you don't know what port it connects to
    *   @param QString cableMapKey the YigNetworkChannel map key for the cable
    *   @return bool Test for whether the cable was found, and subsequently deleted
    */
    bool removeCable(QString cableMapKey);

    /** Checks collisions and auto connects/disconnects the appropriate cables*/
    void autoCable();

    /**
    *   setParam1 Set this YigSynth's first parameter
    *   @param float param1 The new value to which the first parameter is assigned
    */
    void setParam1(float value);

    /**
    *   setParam2 Set this YigSynth's second parameter
    *   @param float param2 The new value to which the second parameter is assigned
    */
    void setParam2(float value);

    /**
    *   syncParam1 Set this YigSynth's first parameter from the network, rotating the mod circle, but NOT emitting a change
    *   @param float param1 The new value to which the first parameter is assigned
    */
    void syncParam1(float value);

    /**
    *   syncParam2 Set this YigSynth's second parameter from the network, rotating the mod circle, but NOT emitting a change
    *   @param float param2 The new value to which the second parameter is assigned
    */
    void syncParam2(float value);

    /**
    *   called when the selected state of the synth changes locally
    *   @param QString the userName of the user that has selected or unselected the synth
    */
    void setselectingUser(QString userName);

    /**
    *   broadcastParams cause this synth graphic to emit the paramChanged signal;
    */
    void broadcastParams();

    /** overridden to give this class a unique type id */
    int type() const;

    /** overridden to allow for custom highlighting */
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

    /** list of the network channel map keys of the current selected synths */
    QList<QString> selectedSynthKeys;

    /** the username of the user, if any, that has this synth selected */
    QString selectingUser;

    /** the static size of all the synth graphics */
    //static const int ELLIPSE_SIZE = 51;
    static const int ELLIPSE_SIZE = 50;

    /** the static size of all the synth graphics */
    //static const int SMALL_ELLIPSE_SIZE = 21;
    static const int SMALL_ELLIPSE_SIZE = 22;

    /** size of the mod input ellipse */
    //static const int MOD_ELLIPSE_SIZE = 30;
    static const int MOD_ELLIPSE_SIZE = 46;

    /** size of the audio input ellipse */
    //static const int AUDIO_ELLIPSE_SIZE = 49;
    static const int AUDIO_ELLIPSE_SIZE = 30;

    /** offset for the ellipse triangle to set it near the middle of the synth ellipse */
    static const int TRI_OFFSET_X = 7;
    static const int TRI_OFFSET_Y = 9;

    /** used to place ellipses in equilateral triangle */
    static const double SQRT_THREE;

    /** the static point on the ellipse (relative) where the audio input goes */
    //static const double AUDIO_INPUT_X = TRI_OFFSET_X + (SMALL_ELLIPSE_SIZE/2);
    //static const double AUDIO_INPUT_Y = TRI_OFFSET_Y + (SMALL_ELLIPSE_SIZE/2);
    static const double AUDIO_INPUT_X;
    static const double AUDIO_INPUT_Y;

    /** the static point on the ellipse (relative) where the audio output goes */
    //static const double AUDIO_OUTPUT_X = TRI_OFFSET_X + 7.5 + (SMALL_ELLIPSE_SIZE/2);
    //static const double AUDIO_OUTPUT_Y = TRI_OFFSET_Y + 12.9903811 + (SMALL_ELLIPSE_SIZE/2);
    static const double AUDIO_OUTPUT_X;
    static const double AUDIO_OUTPUT_Y;

    /** the static point on the ellipse (relative) where the mod input goes */
    //static const double MOD_INPUT_X = TRI_OFFSET_X + 15 + (SMALL_ELLIPSE_SIZE/2);
    //static const double MOD_INPUT_Y = TRI_OFFSET_Y + (SMALL_ELLIPSE_SIZE/2);
    static const double MOD_INPUT_X;
    static const double MOD_INPUT_Y;

    /** Size of the mod field ellipses that form the outer parts of the synths */
    static const int MOD_FIELD_SIZE;
    static const int MOD_FIELD_2_SIZE;

    static QPixmap* circleImage;
    static bool staticImageInitBool;
    static void initCircleImage();

    /**
    *   @enum EllipseType Defines the three different type of ellipses in the center, This may be phased out for all auto cables
    *   @value AUDIO_INPUT  The top left ellipse
    *   @value AUDIO_OUTPUT The bottom ellipse
    *   @value MOD_INPUT The top right ellipse
    */
    enum EllipseType {
        AUDIO_INPUT,
        AUDIO_OUTPUT,
        MOD_INPUT
    };

    /**
    *   @enum ParamType defines the type of modulation of parameter for synths
    *   @value Param1 Assigned to the outer circles of the synths
    *   @value Param2 Assigned to the inner circles of the synths
    */
    enum ParamType {
        Param1,
        Param2
    };

signals:

    /** emitted when the position of this ellipse has changed
    *   @param QString mapKey the map key for this synth graphic
    *   @param const QPointF& point the new location of the synth graphic
    */
    void posChanged( QString mapKey, const QPointF& point );

    /**
    *   emitted when the position of this ellipse has changed, used to update the oscilliscope placement
    *   @param int audioIndex The audio output index for this synth, this is used as a hash map key in the opengl thread
    *   @param QPointF& the SCENE position of this synth
    */
    void posChanged( int audioIndex, const QPointF& point);

    /**
    *   synthKeyboardDeleted emitted when the local user attempts to delete this object from the keyboard
    *   @param QString mapKey the map key for this synth graphic
    */
    //void synthKeyboardDeleted(QString mapKey);

    /**
    *   ellipseClicked emitted when the local user clicks on an audioOutput ellipse
    *   @param QString mapKey the map key for this synth graphic
    *   @param EllipseType the type of ellipse, either AUDIO_INPUT, AUDIO_OUTPUT, or MOD_INPUT
    *   @param const QPointF& point the CENTER of the output ellipse
    */
    //void ellipseClicked(QString mapKey, YigSynthGraphic::EllipseType ellipseType, QPointF point);

    /**
    *   autoCableTriggered emitted for automatic cable creation from ellipse overlapping
    *   @param QString inputKey The synthGraphicMap key for the input synth
    *   @param QString outputKey The synthGraphicMap key for the output synth
    *   @param YigCableGraphic::CableOutputType outputType  The output type of the cable AUDIO or MODULATION
    */
    void autoCableTriggered(QString inputKey, QString outputKey, YigCableGraphic::CableOutputType outputType);

    /**
    *   param1Change emitted when the user changes the outer modulation field
    *   @param float value The new value for the synth
    *   @param bool Tells whether to transmit the value change or not
    */
    void param1Changed(float value, bool networkChange);

    /**
    *   param2Change emitted when the user changes the inner modulation field
    *   @param float value The new value for the synth
    *   @param bool Tells whether to transmit the value change or not
    */
    void param2Changed(float value, bool networkChange);

    /**
    *   emitted when a parameter for this synth has changed
    *   @param QString mapKey The key for the synth that has changed
    *   @param YigSynthGraphic::ParamType The type of parameter (param1 or param2)
    *   @param float The new value for the parameter
    */
    void paramChanged(QString mapKey, YigSynthGraphic::ParamType paramType, float value);

    /**
    *   emitted when the selected state of the synth changes locally
    *   @param QString the map key for the synth
    *   @param int The selected state bool represented as an int
    */
    void selectedChanged(QString mapKey, int selected);

protected:

    /**
    *   mouseMoveEvent reimplemented so we can add networking of position
    *   @param QGraphicsSceneMouseEvent *event the event that moves this item
    */
    void mouseMoveEvent( QGraphicsSceneMouseEvent *event );

    /**
    *   itemChange reimplemented so we can add networking and update cable placement
    *   @param GraphicsItemChange change
    *   @param const QVariant &value
    */
    QVariant itemChange(GraphicsItemChange change, const QVariant &value);

    /** hover used to highlight synth for grabbing */
    void hoverEnterEvent(QGraphicsSceneHoverEvent *event);

    /** hover leave used to return it back to its original state*/
    void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);

    /** change cursor to display movement icon */
    void mousePressEvent(QGraphicsSceneMouseEvent *event);

    /** change cursor back */
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

private:

    /**
    *   updateCablePositions update the attached cable positions based on what type of cable they are
    */
    void updateCablePositions();

    /**
    *   deleteCables Iterate through the cable lists and call delete on each cable
    */
    void deleteCables();

    /** Ellipse for the audio input to the synths */
    QGraphicsEllipseItem* audioInput;
    /** Ellipse for the audio output to the synths */
    //QGraphicsEllipseItem* audioOutput;
    /** Ellipse for the modulation input to the synths */
    QGraphicsEllipseItem* modInput;
    /** Larger circle around synth that defines autoconnect range AND allows for rotation based modulation */
    QGraphicsEllipseItem* modField1;
    /** 2nd larger circle around synth that defines the rotation based modulation for the second argument of the synth */
    QGraphicsEllipseItem* modField2;
    /** Displays the name of the type of synth object */
    QGraphicsSimpleTextItem* synthLabel;
    /** item for auto cable collisions */
    //QGraphicsLineItem* audioInArrow;
    /** item for auto cable collisions */
    //QGraphicsLineItem* audioOutArrow;
    /** item for auto cable collisions */
    //QGraphicsLineItem* modInArrow;
    /** item for auto cable collisions */
    QGraphicsEllipseItem* audioInArrow;
    /** item for auto cable collisions */
    QGraphicsEllipseItem* modInArrow;
    /** item for auto cable collisions */
    //QGraphicsEllipseItem* audioOutArrow;
    /** item for auto cable collisions */
    //QGraphicsEllipseItem* modOutArrow;
    /** draws the outline of the ellipse */
    QPen pen;
    /** fills the color of the ellipse */
    QBrush brush;
    /** the actual fill/outline color */
    QColor fillColor,outlineColor;
    /** key for this item to the YigNetworkChannel map */
    QString mMapKey;
    /** The type of synth this graphic is portraying */
    QString mSynthType;
    /**  list of mapKeys to cables */
    QList<YigCableGraphic*> audioInputCableList, audioOutputCableList, modInputCableList;
    /** splits up auto cable updates by a division */
    int autoCableCounter;
    /** values for the two modulatable parameters of the synth */
    float param1, param2;
    /** the output bus for this synth */
    int audioOutBus;
};


/**
*   YigEllipseItem triggers its coordinating YigSynthGraphic container to emit a signal for networking
*/
class YigEllipseItem : public QGraphicsEllipseItem
{
public:

    /**
    *   YigEllipseItem defines the ellipse that we use to represent audio elements in the gui
    *   @param YigSynthGraphic* synthGraphic pointer to the parent YigSynthGraphic. We use this to emit a signal from the parent
    *   @param qreal x Beginning x coordinate
    *   @param qreal y Beginning y coordinate
    *   @param qreal width Width of the ellipse
    *   @param qreal height Height of the ellipse
    *   @param QGraphicsItem* parent Parent graphics item
    */
    explicit YigEllipseItem(YigSynthGraphic* synthGraphic, qreal x = 0, qreal y = 0, qreal width = 0, qreal height = 0,
                            QGraphicsItem* parent = 0);


    //enum { Type = UserType + 1 };
    //int type() const;

    /**
    *   setPos sets the position of the YigEllipseItem with x/y coordinates
    *   @param qreal x The new X coordinate
    *   @param qreal y The new Y coordinate
    */
    void setPos( qreal x, qreal y );

    /**
    *   setPos sets the position of the YigEllipseItem with a float Point
    *   @param QPointF &pos The new point(x,y) of the ellipse.
    */
    void setPos( const QPointF &pos );

    /**
    *   moveBy moves the position of the YigEllipseItem by the x/y amount
    *   @param qreal dx Move the Y coordinate of the YigEllipseItem by dx.
    *   @param qreal dy Move the Y coordinate of the YigEllipseItem by dy.
    */
    void moveBy( qreal dx, qreal dy );

    /** overridden from base graphic, called when the local user attempts to delete this ellipse from the keyboard */
    //void keyboardDelete();


protected:

    /**
    *   mouseMoveEvent reimplemented so we can add networking
    *   @param QGraphicsSceneMouseEvent *event the event that moves this item
    */
    void mouseMoveEvent( QGraphicsSceneMouseEvent *event );

    /**
    *   mousePressEvent overridden to implement drag/drop cable creation
    *   @param QGraphicsSceneMouseEvent *event the mouse click event
    */
    //void mousePressEvent(QGraphicsSceneMouseEvent *event);

private:

    /** YigBaseGraphic pointer to the parent YigBaseGraphic. We use this to emit a signal from the parent. Don't delete! */
    YigBaseGraphic* parentGraphic;
};


/**
*   YigModField
*   Defines the outer rings around the synth that can be rotated to modulate the synth
*/
class YigModField : public YigBaseGraphic, public QGraphicsEllipseItem
{
    Q_OBJECT

public:

    /**
    *   YigModField constructor
    *   @param YigSynthGraphic* synthGraphic pointer to the parent YigSynthGraphic. We use this to emit a signal from the parent
    *   @param float the randomozied parameter that this mod field should be initialized to
    *   @param qreal x Beginning x coordinate
    *   @param qreal y Beginning y coordinate
    *   @param qreal width Width of the ellipse
    *   @param qreal height Height of the ellipse
    *   @param QGraphicsItem* parent Parent graphics item
    */
    YigModField(YigSynthGraphic* synthGraphic, float param, qreal x = 0, qreal y = 0, qreal width = 0, qreal height = 0,
                QGraphicsItem* parent = 0);

    /**
    *   YigModField deconstructor
    */
    ~YigModField();

    /** Overridden method calls the parent graphics' keyboard delete method */
    //void keyboardDelete();

    /**
    *   updatePos Overridden from parent, but does nothing for mod field...so far...
    *   @param const QPointF &pos New position
    */
    void updatePos(const QPointF &pos);

    /**
    *   syncParam1 Set this YigSynth's first parameter from the network, rotating the mod circle, but NOT emitting a change
    *   @param float param1 The new value to which the first parameter is assigned
    */
    void syncParam1(float value);

    /**
    *   itemClick Overridden from parent, but does nothing for mod field...so far...
    *   @param QGraphicsItem* item The item that was clicked on
    */
    //void itemClick(QGraphicsItem* item);

    /** getModElectron() Returns a pointer to the child mod ellipse
    *   @return QGraphicsEllipseItem* The attached mod electron ellipse
    */
    QGraphicsEllipseItem* getModElectron();

    /**
    *   getParentGraphic get the parent synth graphic for this mod field
    *   @return YigSynthGraphic* Pointer to the graphic
    */
    YigSynthGraphic* getParentGraphic();

    /** overridden type allows us to differentiate types during collisions */
    int type() const;

    /** overridden to allot for custom highlight painting */
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

    /** overridden to update and network selection changes */
    virtual void setSelected(bool selected);

    /** line that displays the absolute value of the parameter */
    QGraphicsPolygonItem modTriangle;

protected:

    /** overidden to allow for rotation modulation */
    void mouseMoveEvent(QGraphicsSceneMouseEvent *event);

    /** overridden to add curor change */
    void mousePressEvent(QGraphicsSceneMouseEvent *event);

    /** overridden to add curor change */
    void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);

    /**
    *   updateParameter
    *   @param float parameter The new value
    */
    virtual void updateParameter(float parameter);

    /** Pointer to the parent synth graphic */
    YigSynthGraphic* parentGraphic;

    /** Ellipse parented to modField that helps display the mod amount on a particular synth */
    QGraphicsEllipseItem* modElectron;

    /** overridden to update and network selection changes */
    virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value);

private:

    /**
    *   angleFromCenter calculates an angle from the center point of the ellipse, use for mouse modulation values
    *   @param QPointF The point to calculate the angle from
    *   @return qreal the Angle of the point against the horizontal plane, in degrees
    */
    qreal angleFromCenter(QPointF point);

    /** method that creates the mod value circle */
    void createModCircle();

    /** redraws the mod arc according to the curren mod value for this synth */
    void redrawArc();

    /** current rotation angle of this mod field */
    qreal rotationAngle;
    /** arc representing the mod value */
    QGraphicsPolygonItem modArc;
    /** background strange attractor */
    QGraphicsPixmapItem yigAttractor;
};


/**
*   YigModField2
*   Subclass of YigModField to give the inner ring a different type than the outer ring, increasing performance for auto cables
*/
class YigModField2 : public YigModField
{

public:

    /**
    *   YigModField2 constructor
    *   @param YigSynthGraphic* synthGraphic pointer to the parent YigSynthGraphic. We use this to emit a signal from the parent
    *   @param float the randomozied parameter that this mod field should be initialized to
    *   @param qreal x Beginning x coordinate
    *   @param qreal y Beginning y coordinate
    *   @param qreal width Width of the ellipse
    *   @param qreal height Height of the ellipse
    *   @param QGraphicsItem* parent Parent graphics item
    */
    YigModField2(YigSynthGraphic* synthGraphic, float param, qreal x = 0, qreal y = 0, qreal width = 0, qreal height = 0,
                QGraphicsItem* parent = 0);

    /** overridden type allows us to differentiate types during collisions */
    int type() const;

    /**
    *   syncParam2 Set this YigSynth's second parameter from the network, rotating the mod circle, but NOT emitting a change
    *   @param float param2 The new value to which the second parameter is assigned
    */
    void syncParam2(float value);

protected:

    /**
    *   updateParameter Overridden to allow for second parameter modulation
    *   @param float parameter The new value
    */
    void updateParameter(float parameter);

private:

    /** overridden method that creates the mod value circle */
    void createModCircle();

};

///////////////////
// YigSignalArrow
//////////////////

/**
*   YigSignalArrow
*   YigSignalArrow Defines the arrows around a synth that define what kind of auto cable connections will happen
*/
class YigSignalArrow : public QGraphicsEllipseItem
{

public:

    /**
    *   YigSignalArrow Defines the arrows around a synth that define what kind of auto cable connections will happen
    *   @param YigSynthGraphic::EllipseType arrowType the type of this arrow, audio in/audio out/mod in
    *   @param YigSynthGraphic* parentGraphic Pointer to the parent synth graphic
    */
    YigSignalArrow(YigSynthGraphic::EllipseType arrowType, YigSynthGraphic* parentGraphic);

    ~YigSignalArrow();

    /**
    *   Returns the type of this arrow, audio in/audio out/mod in
    *   @return YigSynthGraphic::EllipseType The type of this arrow
    */
    YigSynthGraphic::EllipseType getArrowType();

    /**
    *   getParentGraphic get the parent synth graphic for this mod field
    *   @return YigSynthGraphic* Pointer to the graphic
    */
    YigSynthGraphic* getParentGraphic();

    /** overridden type allows us to differentiate types during collisions */
    int type() const;

    /** defines the size of the arrow lines */
    static const int ARROW_SIZE = 16;

private:

    /** called to setup the arrow according to its type */
    void setupArrow();

    /** parentGraphic Pointer to the parent synth graphic */
    YigSynthGraphic* mParentGraphic;
    ///** The line that completes the  arrow for this item */
    //QGraphicsLineItem* halfArrow;
    /** arrow indicator item*/
    QGraphicsPolygonItem arrow;
    /** define what kind of auto cable connections will happen and the direction the arrow points */
    YigSynthGraphic::EllipseType mArrowType;
};

#endif // YIGSYNTHGRAPHIC_H
